﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.Core;
using Castle.Windsor;
using Castle.MicroKernel;
using NLog;
using RB = Rhino.Commons.Binsor;
using NGinnBPM.Lib;
using NGinnBPM.Lib.Interfaces;
using NGinnBPM.Lib.Interfaces.Worklist;
using NGinnBPM.MessageBus;
using NGinnBPM.Runtime;
using NGinnBPM.Services;
using Microsoft.Practices.ServiceLocation;
using CommonServiceLocator.WindsorAdapter;
using Castle.MicroKernel.Registration;

namespace NGinnBPM.HostConfig
{
    /// <summary>
    /// NGinn host configurator using Castle Windsor container
    /// and Binsor component setup script
    /// </summary>
    public class BinsorNGinnHostConfigurator
    {
        private IWindsorContainer _wc;
        private static Logger log = LogManager.GetCurrentClassLogger();
        private string _setupScript = "assembly://NGinnBPM.HostConfig/NGinnBPM.HostConfig.DefaultNGinnConfig.boo";
        private NGinnHost _host;

        public static BinsorNGinnHostConfigurator BeginConfig()
        {
            WindsorContainer wc = new WindsorContainer();
            return new BinsorNGinnHostConfigurator(wc);
        }

        public static BinsorNGinnHostConfigurator BeginConfig(WindsorContainer wc)
        {
            return new BinsorNGinnHostConfigurator(wc);
        }

        protected BinsorNGinnHostConfigurator(IWindsorContainer wc)
        {
            _wc = wc;
        }

        /// <summary>
        /// Set the Binsor configuration file name used for 
        /// NGinn component setup. If you don't call that, default config is assumed.
        /// Use Windsor resource Uri syntax, like file://path.to.config.boo for files
        /// and assembly://Assembly.Name/Resource.Path for embedded resources
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public BinsorNGinnHostConfigurator SetComponentSetupScript(string fileName)
        {
            _setupScript = fileName;
            return this;
        }

        /// <summary>
        /// Set NGinn configuration options
        /// </summary>
        /// <param name="options"></param>
        /// <returns></returns>
        public BinsorNGinnHostConfigurator SetConfigurationOptions(IDictionary<string, string> options)
        {
            SetConfigProvider(new NGinnBPM.Lib.Util.DictionaryConfigProvider(new Dictionary<string, string>(options)));
            return this;
        }

        /// <summary>
        /// Set configuration provider used to retrieve config options
        /// </summary>
        /// <param name="provider"></param>
        /// <returns></returns>
        public BinsorNGinnHostConfigurator SetConfigProvider(NGinnBPM.Lib.Interfaces.IConfigParameterProvider provider)
        {
            _wc.Register(Component.For<IConfigParameterProvider>().Instance(provider));
            return this;
        }

        public BinsorNGinnHostConfigurator CustomizeContainer(Action<IWindsorContainer> act)
        {
            act(_wc);
            return this;
        }

        public BinsorNGinnHostConfigurator RunComponentSetupScript(string scriptUrl)
        {
            CustomizeContainer(delegate(IWindsorContainer wc)
            {
                Rhino.Commons.Binsor.BooReader.Read(wc, scriptUrl);
            });
            return this;
        }

        /// <summary>
        /// Final config method, sets everything up
        /// </summary>
        /// <returns></returns>
        public BinsorNGinnHostConfigurator FinishConfiguration()
        {
            if (_wc.GetService<IServiceLocator>() == null)
            {
                WindsorServiceLocator wsl = new WindsorServiceLocator(_wc);
                _wc.Register(Component.For<IServiceLocator>().Instance(wsl));
            }
            if (_wc.GetService<IConfigParameterProvider>() == null)
            {
                _wc.Register(Component.For<IConfigParameterProvider>()
                    .ImplementedBy<NGinnBPM.Lib.Util.DefaultConfigProvider>()
                    .LifeStyle.Singleton);
            }
            log.Info("Loading setup script from {0}", _setupScript);
            Rhino.Commons.Binsor.BooReader.Read(_wc, _setupScript);
            _host = new NGinnHost(_wc);
            return this;
        }

        public IWindsorContainer GetContainer()
        {
            return _wc;
        }

        public BinsorNGinnHostConfigurator RegisterNGinnHostForRemoting(string url)
        {
            System.Runtime.Remoting.ObjRef oref = System.Runtime.Remoting.RemotingServices.Marshal(_host, url, typeof(INGinnHost));
            log.Info("Registered nginn host for remoting: {0}", oref.URI);
            return this;
        }

        public INGinnHost NGinnHost
        {
            get
            {
                return _host;
            }
        }

        public IServiceLocator ServiceLocator
        {
            get { return _wc.Resolve<IServiceLocator>(); }
        }
    }
}
